{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {
    "id": "BQ_52GTV6jJy"
   },
   "source": [
    "# OmniSafe Tutorial - Getting Started\n",
    "\n",
    "OmniSafe: https://github.com/PKU-Alignment/omnisafe\n",
    "\n",
    "Documentation: https://omnisafe.readthedocs.io/en/latest/\n",
    "\n",
    "Safety-Gymnasium: https://www.safety-gymnasium.com/\n",
    "\n",
    "[Safety-Gymnasium](https://www.safety-gymnasium.com/) is a highly scalable and customizable Safe Reinforcement Learning library, aiming to deliver a good view of benchmarking Safe Reinforcement Learning (Safe RL) algorithms and a more standardized setting of environments. \n",
    "\n",
    "\n",
    "## Introduction\n",
    "\n",
    "In this tutorial, we will present some fundamental applications of OmniSafe. Through clear and comprehensible examples, we aim to enable OmniSafe to promptly and dependably serve users in their research and production tasks. With the introduction of this chapter and the subsequent tutorials, we aspire to assist you in establishing a comprehensive comprehension of the characteristics and design philosophy of OmniSafe.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "v8ZS9NZsTBia"
   },
   "outputs": [],
   "source": [
    "# Install from PyPI. if you already have OmniSafe installed, please ignore the code in this cell.\n",
    "!pip install omnisafe"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "X_QurlxZ0jDh"
   },
   "outputs": [],
   "source": [
    "# Install OmniSafe and dependencies from source\n",
    "## if you already have OmniSafe installed, please ignore the code in this cell.\n",
    "## Clone the repo\n",
    "!git clone https://github.com/PKU-Alignment/omnisafe\n",
    "%cd omnisafe\n",
    "\n",
    "## Install OmniSafe\n",
    "!pip install -e ."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "rnq3f13hNR1V"
   },
   "source": [
    "## Basic Usage Examples"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "u1dg7h99Nq83"
   },
   "source": [
    "### Train from default configs\n",
    "\n",
    "By executing four lines of code, we can train a SafeRL agent using PPOLag.\n",
    "\n",
    "During the development process, OmniSafe underwent extensive testing and adjustments. The **default hyperparameters** are designed to achieve the best overall performance across as many benchmark environments as possible with **minimal tuning** and **tricks**. For the initial usage, it is advisable to overlook all the details and indulge in the joy of ready-to-use functionality.\n",
    "\n",
    "With the specification of the environment ID and algorithm, you can promptly commence your SafeRL journey.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "cj2GjYy2PqZ_"
   },
   "outputs": [],
   "source": [
    "import omnisafe\n",
    "\n",
    "\n",
    "env_id = 'SafetyPointGoal0-v0'\n",
    "\n",
    "agent = omnisafe.Agent('PPOLag', env_id)\n",
    "agent.learn()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "fIiALZst13F0"
   },
   "source": [
    "The results of the execution will be automatically saved to the directory where you run the Python script.\n",
    "\n"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {
    "id": "F2MZiwk02RWp"
   },
   "source": [
    "### Train from custom dict\n",
    "\n",
    "Hyperparameters in reinforcement learning have a significant impact on performance. After getting a taste of the default settings, you can explore new insights and methods by specifying parameter values through a dictionary when facing specific problems. You can refer to the default parameters and their formats in the GitHub repo [here](https://github.com/PKU-Alignment/omnisafe/tree/main/omnisafe/configs).\n",
    "\n",
    "The following code runs `two epochs` with a total of `2048` interactions and updates the policy every `1024` steps on `SafetyPointGoal1-v0`. The environment and thread parallelism are both set to `1`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "vxbrupSD5cJK"
   },
   "outputs": [],
   "source": [
    "import omnisafe\n",
    "\n",
    "\n",
    "env_id = 'SafetyPointGoal1-v0'\n",
    "custom_cfgs = {\n",
    "    'train_cfgs': {\n",
    "        'total_steps': 2048,\n",
    "        'vector_env_nums': 1,\n",
    "        'parallel': 1,\n",
    "    },\n",
    "    'algo_cfgs': {\n",
    "        'steps_per_epoch': 1024,\n",
    "        'update_iters': 1,\n",
    "    },\n",
    "    'logger_cfgs': {\n",
    "        'use_wandb': False,\n",
    "    },\n",
    "}\n",
    "\n",
    "agent = omnisafe.Agent('TRPO', env_id, custom_cfgs=custom_cfgs)\n",
    "agent.learn()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "27tsIfmo7rd9"
   },
   "source": [
    "### Render and evaluate your policy\n",
    "\n",
    "RL has made significant progress in the past, but in the present day, we believe that the performance of an algorithm should not be solely evaluated by the accumulated reward of the agent. Instead, more emphasis should be placed on whether the learning of the agent results in meaningful behavior, especially when safe factors are taken into consideration. It is crucial to assess whether the agent can actually produce decision sequences with safety constraints.\n",
    "\n",
    "Therefore, OmniSafe supports fast rendering and evaluation of policy models. After training is complete, you can easily visualize the results and complete a one-stop workflow in OmniSafe, saving valuable time."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "BUkkson3Co65"
   },
   "source": [
    "The following line of code will plot the training curve of the agent that has been trained above, displaying the changes of reward and cost over the course of the entire training process with respect to the number of interactions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "w8_WGipx-lA0"
   },
   "outputs": [],
   "source": [
    "agent.plot(smooth=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "o0SDM8tkC2bx"
   },
   "source": [
    "After reviewing the training curve, whether you are satisfied or disappointed with the results, you must be eager to understand the behavior that led to such a curve. Therefore, OmniSafe supports the entire workflow from training to visualization and data analysis."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "gxX84UBkXZ7i"
   },
   "source": [
    "Running visualization on cloud containers necessitates some additional dependencies."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "QuIAV2k7rUzu"
   },
   "outputs": [],
   "source": [
    "%%bash\n",
    "apt-get install libosmesa6-dev\n",
    "apt-get install python3-opengl"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "SWbnhykqZTO1"
   },
   "outputs": [],
   "source": [
    "%env MUJOCO_GL=osmesa\n",
    "%env PYOPENGL_PLATFORM=osmesa"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "bPSz0C8CDApD"
   },
   "outputs": [],
   "source": [
    "agent.render(num_episodes=1, render_mode='rgb_array', width=256, height=256)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "NyEhctNbt2Qx"
   },
   "source": [
    "Try playing the video by inputting the provided file path above!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "0cTAss1RuFYn"
   },
   "outputs": [],
   "source": [
    "import base64\n",
    "from pathlib import Path\n",
    "\n",
    "from IPython import display as ipythondisplay\n",
    "\n",
    "\n",
    "def show_videos(video_path='', prefix=''):\n",
    "    \"\"\"\n",
    "    Taken from https://github.com/eleurent/highway-env\n",
    "\n",
    "    :param video_path: (str) Path to the folder containing videos\n",
    "    :param prefix: (str) Filter the video, showing only the only starting with this prefix\n",
    "    \"\"\"\n",
    "    html = []\n",
    "    for mp4 in Path(video_path).glob(\"{}*.mp4\".format(prefix)):\n",
    "        video_b64 = base64.b64encode(mp4.read_bytes())\n",
    "        html.append(\n",
    "            '''<video alt=\"{}\" autoplay \n",
    "                    loop controls style=\"height: 400px;\">\n",
    "                    <source src=\"data:video/mp4;base64,{}\" type=\"video/mp4\" />\n",
    "                </video>'''.format(\n",
    "                mp4, video_b64.decode('ascii')\n",
    "            )\n",
    "        )\n",
    "    ipythondisplay.display(ipythondisplay.HTML(data=\"<br>\".join(html)))\n",
    "\n",
    "\n",
    "# Please fill the path of folder containing your video which is shown above here\n",
    "show_videos(video_path='')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "muSYZV_-DCLW"
   },
   "source": [
    "Alternatively, you may simply wish to observe how the converged policy performs numerically during testing."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "rMD6j5sADOaz"
   },
   "outputs": [],
   "source": [
    "agent.evaluate(num_episodes=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "yqQmc_ODDY5T"
   },
   "source": [
    "We aim to provide abundant information so that you can gain new insights. Through the aforementioned approach, you can gain a multidimensional understanding of this experiment."
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {
    "id": "TrBRWG9c-2Dw"
   },
   "source": [
    "\n",
    "\n",
    "Certainly, you can also analyze the saved policies from the past. Hey! Try pasting the saved policy path above and running the following script, which is the most common usage of Evaluator. It visualizes all models under the specified experiment path in the given manner. Generally, **this code is more flexible and useful on your personal server than in Colab**.\n",
    "\n",
    "By modifying the code, you can visualize model files under any path. Several crucial parameters are essential to understand for visualization.\n",
    "\n",
    "`render_mode`: This parameter specifies the display mode during visualization, which typically includes `rgb_array`, `depth_array`, `human`. You may encounter difficulties when specifying them on a server without a display, so please refer to [issue72](https://github.com/PKU-Alignment/omnisafe/issues/72) and [issue27](https://github.com/PKU-Alignment/omnisafe/issues/27).\n",
    "\n",
    "`camera_name`: Its value depends on the design of the environment library. In Safety-Gymnasium, it supports [these](https://www.safety-gymnasium.com/en/latest/api/builder.html#safety_gymnasium.builder.Builder.__init__).\n",
    "\n",
    "`width, height`: These two parameters are used to specify the image resolution. The larger the numerical value, the higher the image quality, and the greater the demand for hardware resources. We suggest you try different values according to your hardware resources and choose the one that meets your needs."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "vaVHC5WK-1CT"
   },
   "outputs": [],
   "source": [
    "# Single Python File\n",
    "import os\n",
    "import omnisafe\n",
    "\n",
    "# Just fill your experiment's log directory in here.\n",
    "# Such as: ~/omnisafe/examples/runs/PPOLag-{SafetyPointGoal1-v0}/seed-000-2023-03-07-20-25-48\n",
    "LOG_DIR = './runs/PPOLag-{SafetyPointGoal1-v0}/seed-000-2023-04-01-02-44-35'\n",
    "evaluator = omnisafe.Evaluator(render_mode='rgb_array')\n",
    "for item in os.scandir(os.path.join(LOG_DIR, 'torch_save')):\n",
    "    if item.is_file() and item.name.split('.')[-1] == 'pt':\n",
    "        evaluator.load_saved(\n",
    "            save_dir=LOG_DIR, model_name=item.name, camera_name='track', width=256, height=256\n",
    "        )\n",
    "        evaluator.render(num_episodes=1)\n",
    "        evaluator.evaluate(num_episodes=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "3Gl1f0kgG8ea"
   },
   "source": [
    "### Benchmark your research\n",
    "During the research process, running a large number of experiments is often a laborious and error-prone task. To allow researchers to focus on valuable work, OmniSafe provides the experiment grid module. With it, you can quickly and reliably run a large number of experiments by providing all the possible parameter values of interest. This module is incredibly convenient, whether you're creating baselines for your algorithm or tuning parameters.\n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {
    "id": "gWxhptNhIsy6"
   },
   "source": [
    "From an implementation perspective, you can simply understand it as:\n",
    "\n",
    "1. Generating all feasible combinations of parameters.\n",
    "2. Using Python's multiprocessing tool to execute the same function with different parameters simultaneously, according to the specified parallelism.\n",
    "\n",
    "Therefore, you need to define a function that needs to be executed through `experiment_grid`. We have already done this for you, and you can simply copy our [example](https://github.com/PKU-Alignment/omnisafe/tree/main/examples), or you can refer to the examples on GitHub, which contain code examples for almost all the content discussed in this section.\n",
    "\n",
    "**Note**: You don't need to pay attention to the implementation details of this code, and only need to modify it when you have highly customized requirements.\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "o4ZijAqvIWMK"
   },
   "outputs": [],
   "source": [
    "\"\"\"Example of training a policy from exp-x config with OmniSafe.\"\"\"\n",
    "\n",
    "import os\n",
    "import sys\n",
    "import warnings\n",
    "\n",
    "import torch\n",
    "\n",
    "import omnisafe\n",
    "from omnisafe.common.experiment_grid import ExperimentGrid\n",
    "from omnisafe.typing import NamedTuple, Tuple\n",
    "\n",
    "\n",
    "def train(\n",
    "    exp_id: str, algo: str, env_id: str, custom_cfgs: NamedTuple\n",
    ") -> Tuple[float, float, float]:\n",
    "    \"\"\"Train a policy from exp-x config with OmniSafe.\n",
    "\n",
    "    Args:\n",
    "        exp_id (str): Experiment ID.\n",
    "        algo (str): Algorithm to train.\n",
    "        env_id (str): The name of test environment.\n",
    "        custom_cfgs (NamedTuple): Custom configurations.\n",
    "        num_threads (int, optional): Number of threads. Defaults to 6.\n",
    "    \"\"\"\n",
    "    terminal_log_name = 'terminal.log'\n",
    "    error_log_name = 'error.log'\n",
    "    if 'seed' in custom_cfgs:\n",
    "        terminal_log_name = f'seed{custom_cfgs[\"seed\"]}_{terminal_log_name}'\n",
    "        error_log_name = f'seed{custom_cfgs[\"seed\"]}_{error_log_name}'\n",
    "    sys.stdout = sys.__stdout__\n",
    "    sys.stderr = sys.__stderr__\n",
    "    print(f'exp-x: {exp_id} is training...')\n",
    "    if not os.path.exists(custom_cfgs['logger_cfgs']['log_dir']):\n",
    "        os.makedirs(custom_cfgs['logger_cfgs']['log_dir'], exist_ok=True)\n",
    "    # pylint: disable-next=consider-using-with\n",
    "    sys.stdout = open(\n",
    "        os.path.join(f'{custom_cfgs[\"logger_cfgs\"][\"log_dir\"]}', terminal_log_name),\n",
    "        'w',\n",
    "        encoding='utf-8',\n",
    "    )\n",
    "    # pylint: disable-next=consider-using-with\n",
    "    sys.stderr = open(\n",
    "        os.path.join(f'{custom_cfgs[\"logger_cfgs\"][\"log_dir\"]}', error_log_name),\n",
    "        'w',\n",
    "        encoding='utf-8',\n",
    "    )\n",
    "    agent = omnisafe.Agent(algo, env_id, custom_cfgs=custom_cfgs)\n",
    "    reward, cost, ep_len = agent.learn()\n",
    "    return reward, cost, ep_len"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "QUX4saAoJoiR"
   },
   "source": [
    "\n",
    "Continuing, you can create an instance of the `experiment grid` and call `eg.add` to specify your parameters. For multiple parameters, please store them in a list.\n",
    "\n",
    "**Note**: whether to call it `eg` depends on the variable name you specify for the `experiment grid` instance."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "t1f4lMz6JtK2"
   },
   "outputs": [],
   "source": [
    "eg = ExperimentGrid(exp_name='Tutorial_benchmark')\n",
    "\n",
    "# Set the algorithms.\n",
    "base_policy = ['PolicyGradient', 'NaturalPG', 'TRPO', 'PPO']\n",
    "\n",
    "# Set the environments.\n",
    "mujoco_envs = [\n",
    "    'SafetyAntVelocity-v1',\n",
    "    'SafetyHopperVelocity-v1',\n",
    "    'SafetyHumanoidVelocity-v1',\n",
    "]\n",
    "eg.add('env_id', mujoco_envs)\n",
    "eg.add('algo', base_policy)\n",
    "eg.add('logger_cfgs:use_wandb', [False])\n",
    "eg.add('train_cfgs:vector_env_nums', [1])\n",
    "eg.add('train_cfgs:torch_threads', [1])\n",
    "eg.add('train_cfgs:total_steps', [2048])\n",
    "eg.add('algo_cfgs:steps_per_epoch', [1024])\n",
    "eg.add('seed', [0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "nYVD1pL1LK96"
   },
   "source": [
    "CUDA is a powerful acceleration tool for machine learning, and we provide support for it as well. You can evenly distribute your experiments among multiple GPUs for execution. Here is an example to illustrate this. (As Colab does not support it, we have commented out the code. You may try to use it on your own machine.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "Om1yCcDgLYVX"
   },
   "outputs": [],
   "source": [
    "# # Set the device.\n",
    "# avaliable_gpus = [num for num in range(torch.cuda.device_count())]\n",
    "# gpu_id = [0, 1, 2, 3]\n",
    "# # if you want to use CPU, please set gpu_id = None\n",
    "# # gpu_id = None\n",
    "\n",
    "# if set(gpu_id) > set(avaliable_gpus):\n",
    "#     warnings.warn('The GPU ID is not available, use CPU instead.')\n",
    "#     gpu_id = None"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "I8f2t0PZLfOK"
   },
   "source": [
    "Next, you can specify the number of processes to run in parallel and make full use of your machine's capabilities!\n",
    "\n",
    "One important point to note is that we recommend setting the value of `num_pool` to a number that can evenly divide the total number of tasks. This ensures that your computer's workload is evenly distributed at all times, maximizing its computing power."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "VvinJv_3Lnv8"
   },
   "outputs": [],
   "source": [
    "# total experiment num must can be divided by num_pool\n",
    "# meanwhile, users should decide this value according to their machine\n",
    "eg.run(train, num_pool=12)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "PfexgqIIMBz9"
   },
   "source": [
    "If you are using CUDA, the calling method will be slightly different, as you need to pass in `gpu_id`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "Gf5o7vx6MFd5"
   },
   "outputs": [],
   "source": [
    "# eg.run(train, num_pool=12, gpu_id=gpu_id)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "Jcx0SRyQKm51"
   },
   "source": [
    "\n",
    "Once the training is complete, you can use various data analysis tools mentioned earlier to analyze the experimental results from different perspectives. These tools are independent modules that can be called either simultaneously during training or separately after training. We will explain in detail how to **use and combine** these modules flexibly in later sections.\n",
    "\n",
    "In the following code block, you can specify a parameter and analyze the impact of its different values on performance.\n",
    "\n",
    "`parameter`: specifies the parameter whose impact needs to be analyzed.\n",
    "\n",
    "`values`: specifies several values that need to be displayed on the same graph for comparison.\n",
    "\n",
    "`compare_num`: specifies the maximum number of values to be displayed on the same graph for comparison.\n",
    "\n",
    "`cost_limit`: specifies the cost threshold to be plotted on the graph.\n",
    "\n",
    "**Note**: `values` and `compare_num` are conflicting parameters and cannot be specified simultaneously. If both are set to None, `compare_num` will default to the maximum feasible value.\n",
    "\n",
    "Here are two possible ways to use this functionality:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "Ii2gOSASOYtH"
   },
   "source": [
    "1. Analyzing specified parameter values.\n",
    "\n",
    "We have completed our benchmark on three different environments, and now we want to compare the performance of PPO and PolicyGradient on these environments."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "lOOQkviDKovM"
   },
   "outputs": [],
   "source": [
    "# just fill in the name of the parameter of which value you want to compare.\n",
    "# then you can specify the value of the parameter you want to compare,\n",
    "# or you can just specify how many values you want to compare in single graph at most,\n",
    "# and the function will automatically generate all possible combinations of the graph.\n",
    "# but the two mode can not be used at the same time.\n",
    "eg.analyze(parameter='algo', values=['PPO', 'PolicyGradient'], compare_num=None, cost_limit=None)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "__2JC1gCO4iY"
   },
   "source": [
    "2. Comparing all possible scenarios to find the best algorithm.\n",
    "\n",
    "After a hard day's work, we don't want to look at confusing graphs. So let's give our brains a break. You can ask OmniSafe to generate all possible graphs containing up to three algorithms on a single image. OmniSafe will automatically generate all possible graphs with three algorithms in your current experiment"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "2u22rNv7M4HP"
   },
   "outputs": [],
   "source": [
    "eg.analyze(parameter='algo', values=None, compare_num=3, cost_limit=None)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "u-Tsi7S8QS2t"
   },
   "source": [
    "Of course, as before, you can also visualize all models. Leave all the tedious work to OmniSafe and just review the final results.\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "K6bHQYqfQdXY"
   },
   "outputs": [],
   "source": [
    "eg.render(num_episodes=1, render_mode='rgb_array', width=256, height=256)\n",
    "eg.evaluate(num_episodes=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "CeEEKZWtSVdH"
   },
   "source": [
    "Certainly, you can also use Python code to analyze past experiments using this tool. Here's a simple example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "AL36B-XySdw2"
   },
   "outputs": [],
   "source": [
    "# Single Python File\n",
    "from omnisafe.common.statistics_tools import StatisticsTools\n",
    "\n",
    "\n",
    "# just fill in the path in which experiment grid runs.\n",
    "path = ''\n",
    "st = StatisticsTools()\n",
    "st.load_source(path)\n",
    "# just fill in the name of the parameter of which value you want to compare.\n",
    "# then you can specify the value of the parameter you want to compare,\n",
    "# or you can just specify how many values you want to compare in single graph at most,\n",
    "# and the function will automatically generate all possible combinations of the graph.\n",
    "# but the two mode can not be used at the same time.\n",
    "st.draw_graph(parameter='', values=None, compare_num=2, cost_limit=None, show_image=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "do9X-8qrTDSG"
   },
   "source": [
    "After reviewing the above example and explanation, you have acquired the fundamental usage of OmniSafe. In the following section, we will introduce you to the **CLI** tool in OmniSafe."
   ]
  }
 ],
 "metadata": {
  "colab": {
   "provenance": []
  },
  "kernelspec": {
   "display_name": "Python 3",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.16"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
